/**
  ******************************************************************************
  * @file    eeprom.c 
  * @author  make
  * @version V1.0.0
  * @date    2021.10.29
  * @brief   src program body
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "bsp.h"
#include "tskcfg.h"

#define SI2C_ACK                0
#define SI2C_NACK               1

#define EEPROM_WRITE_ADDR       (0xA0u)
#define EEPROM_READ_ADDR        (0xA1u)

#define EEPROM_PAGE_SIZE        (16)

#define SDA_PIN                 GPIO_PIN_3
#define SDA_GPIO_PORT           GPIOA
#define sda_h()                 (SDA_GPIO_PORT->BSRR   = SDA_PIN) 
#define sda_l()                 (SDA_GPIO_PORT->BRR    = SDA_PIN)
#define sda_r()                 (SDA_GPIO_PORT->IDR    & SDA_PIN)

#define SCL_PIN                 GPIO_PIN_4
#define SCL_GPIO_PORT           GPIOA
#define scl_h()                 (SCL_GPIO_PORT->BSRR   = SCL_PIN)
#define scl_l()                 (SCL_GPIO_PORT->BRR    = SCL_PIN)

#define WP_PIN                  GPIO_PIN_5
#define WP_GPIO_PORT            GPIOA
#define wp_h()                  (WP_GPIO_PORT->BSRR   = WP_PIN)
#define wp_l()                  (WP_GPIO_PORT->BRR    = WP_PIN)

void eEProm_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOB_CLK_ENABLE();
	
	sda_l();
	scl_l();
	wp_h();
  
	GPIO_InitStruct.Pin   = SDA_PIN|SCL_PIN;
	GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Pull  = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  
  GPIO_InitStruct.Pin   = WP_PIN;
	GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.Pull  = GPIO_PULLUP;
	HAL_GPIO_Init(WP_GPIO_PORT, &GPIO_InitStruct);
}

inline static void sda_to_in(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	GPIO_InitStruct.Pin   = SDA_PIN;
	GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull  = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

inline static void sda_to_out(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	GPIO_InitStruct.Pin   = SDA_PIN;
	GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Pull  = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

static inline void si2c_delay(void)
{
  uint16_t delay_time = 8;
	    
	while(delay_time--); /* 8~359k */
}

inline static void si2c_start(void)
{
  sda_h();
	scl_h();
  si2c_delay();
  
	sda_l();      /* sda一个下降沿 */
  si2c_delay();
	
	scl_l();
  si2c_delay();
}

inline static void si2c_stop(void)
{
	sda_l();
	scl_h();
	si2c_delay();
	
	sda_h();      /* sda一个上升沿 */
  si2c_delay();
}

inline static uint8_t si2c_write_byte(uint8_t dat)  /* 18 */
{
	volatile uint8_t i, ack;
	
	scl_l();                   /* 保证scl处于低 */
	
	for(i=0; i<8; i++)
	{
		if(0x80 & dat)
		{
			sda_h();
		}
		else
		{
			sda_l();
		}
		
		dat <<= 1;
		
		scl_h();                 /* scl拉高 锁存数据 */
    si2c_delay();
		
		scl_l();                 /* scl拉低 准备数据 */
    si2c_delay();
	}

  sda_to_in();               /*释放data*/
  
  scl_h();                   /*第9个时钟的上升沿*/
	si2c_delay();
	
  if(sda_r() != 0)
	{
		ack = SI2C_NACK;
	}
	else
	{
		ack = SI2C_ACK;
	}
	
  scl_l();                   /*第9个时钟的下降沿*/
	sda_to_out();              /*重新控制总线*/
//  si2c_delay();
	
	si2c_delay();              /* 字节间延迟 */
	
  return ack;
}

inline static uint8_t si2c_read_byte(volatile uint8_t ack)
{
	volatile uint8_t i;
	volatile uint8_t dat;
	
	dat = 0;
	
	scl_l();             /* 保证scl处于低 */
	
	sda_to_in();
	
	for(i=0; i<8; i++)
	{
		scl_h();                  /* scl拉高后,读出数据 */
    si2c_delay();
		
		dat <<= 1;
		
		if(sda_r() != 0)
		{
			dat |= 0x01;
		}
		else
		{
			dat &= 0xfe;
		}
		
		scl_l();                  /* scl拉低,待对方准备数据 */
    si2c_delay();
	}
	
	sda_to_out();
	
	if(ack != SI2C_ACK)
	{
		sda_h();  /* SI2C_NACK */
	}
	else
	{
		sda_l();  /* SI2C_ACK */
	}
	
  scl_h();
  si2c_delay();              /*发送ack*/
	
  scl_l();
//  si2c_delay();
	
	si2c_delay();              /* 字节间延迟 */
	return dat;
}

uint8_t eeprom_check_rbusy(void)
{
  uint8_t ack;
  
  si2c_start();

  ack = si2c_write_byte(EEPROM_READ_ADDR);
  
  si2c_stop();
  
  return ack;
}

uint8_t eeprom_check_wbusy(void)
{
  uint8_t ack;
  
  si2c_start();

  ack = si2c_write_byte(EEPROM_WRITE_ADDR);
  
  si2c_stop();
  
  return ack;
}

static uint8_t eeprom_page_write(uint16_t addr, const uint8_t *buf, uint16_t len)
{
  uint8_t ack;
  uint8_t i;
  
  /* 确保待写入的内容不会超页的边界 */
  if((addr + len) > ((addr & 0xfff0u) + 0x0010u))
  {
    return 1;
  }
  
  while(eeprom_check_wbusy() != SI2C_ACK)
  {
    vTaskDelay(2);
  }
  
  si2c_start();
  
  ack = si2c_write_byte(EEPROM_WRITE_ADDR | ((uint8_t)((addr & 0x0700u)>>7)));
  
  ack = si2c_write_byte((uint8_t)addr);
  
  for(i=0; i<len; i++)
  {
    ack = si2c_write_byte(buf[i]);
  }
  
  si2c_stop();
  
  return ack;
}

uint8_t eEPRom_Write(uint16_t addr, const uint8_t *buf, uint16_t len)
{
  uint8_t  nalign_num;
  uint8_t  page_num;
  
  wp_l();
  
  if((addr & 0x000Fu) != 0)
  {
    nalign_num = EEPROM_PAGE_SIZE - (addr & 0x000Fu);
    
    eeprom_page_write(addr, buf, nalign_num);

    addr += nalign_num;
    buf  += nalign_num;
    len  -= nalign_num;
  }
  
  page_num = len / EEPROM_PAGE_SIZE;
  
  while(page_num--)
  {
    eeprom_page_write(addr, buf, EEPROM_PAGE_SIZE);
    addr += EEPROM_PAGE_SIZE;
    buf  += EEPROM_PAGE_SIZE;
  }
  
  eeprom_page_write(addr, buf, (len % EEPROM_PAGE_SIZE));
  
  wp_h();
  
  return 0;
}

uint8_t eEPRom_Read(uint16_t addr, uint8_t *buf, uint16_t len)
{
  uint16_t i;
  
  if((buf == NULL) || (len == 0))
  {
    return 1;
  }
  
  while(eeprom_check_wbusy() != SI2C_ACK)
  {
    vTaskDelay(2);
  }
  
  si2c_start();
  
  si2c_write_byte(EEPROM_WRITE_ADDR | ((uint8_t)((addr & 0x0700u)>>7)));
  
  si2c_write_byte((uint8_t)addr);
  
//  si2c_stop();
//  
//  vTaskDelay(2);
  
  si2c_start();
  
  si2c_write_byte(EEPROM_READ_ADDR);
  
  for(i=0; i<len; i++)
  {
    buf[i] = si2c_read_byte(SI2C_ACK);
  }
  
  si2c_stop();
  
  return 0;
}
